/**
 * 
 */
package client;

import gui.CtfGui;

import java.net.Socket;
import java.util.Arrays;

import chat.ChatPacket;
import chat.CtfChat;

import protocol.CtfActionException;
import protocol.CtfDataInputStream;
import protocol.CtfPacket;
import protocol.GameStateTypes;
import protocol.MsgTypes;
import response.BlastBody;
import response.MoveBody;
import structures.CtfObject;
import structures.Maze;
import structures.Player;
import structures.Position;
import update.CellUpdateBody;
import update.CellUpdatePacket;
import update.GameUpdateBody;

/**
 * Thread that listens for responses from the server.
 * @author lilywong
 *
 */
public class Receiver extends Thread {
	CtfDataInputStream cdis;
	
	public void run() {
		while ( true ) {
			CtfPacket p = cdis.receive();
			
			// handle packet
			switch( p.getHeader().getMsgType() ) {
			case MsgTypes.MOVE:
				processMoveResponsePacket(p);
				break;
			case MsgTypes.PICKUP:
				processPickupResponsePacket(p);
				break;
			case MsgTypes.DROP:
				processDropResponsePacket(p);
				break;
			case MsgTypes.BLAST:
				processBlastResponsePacket(p);
				break;
			case MsgTypes.CELL_UPDATE:
				System.out.println("received CELL UPDATE");
				processCellUpdatePacket(p);
				break;
			case MsgTypes.PLAYER_UPDATE:
				System.out.println("received PLAYER UPDATE");
				processPlayerUpdatePacket(p);
				break;
			case MsgTypes.GAME_UPDATE:
				System.out.println("received GAME UPDATE");
				processGameUpdatePacket(p);
				break;
			case MsgTypes.CHAT_MSG_BROADCAST:
				System.out.println("received CHAT BROADCAST");
				processChatMsgBroadcast(p);
				if ( CtfGui.getInstance() != null )
				CtfGui.getInstance().repaint();
				break;
			default:
				System.out.println("received" + p.getHeader().getMsgType());
			}
		}
	}
	
	/** MOVE RESPONSE **/
	public void processMoveResponsePacket(CtfPacket p) {
		try {
			MoveBody mb = new MoveBody( p.getBody() );
			// if not 0,0, update myPlayer
			if ( !mb.getPosition().isInvalid() )
				CtfState.getMyPlayer().setPos(mb.getPosition());
			System.out.println( "move response: " + mb.getPosition().toString() );
			if ( CtfGui.getInstance() != null )
				CtfGui.getInstance().repaint();
		} catch (CtfActionException e) {
			e.printStackTrace();
		}
	}
	
	/** PICKUP RESPONSE **/
	public void processPickupResponsePacket(CtfPacket p) {

	
	}
	
	/** DROP RESPONSE **/
	public void processDropResponsePacket(CtfPacket p) {

	
	}
	
	/** BLAST RESPONSE **/
	public void processBlastResponsePacket(CtfPacket p) {
		try {
			BlastBody bb = new BlastBody( p.getBody() );
			// if not 0, update cell
			if ( !bb.getPosition().isInvalid() ) {
				
			}
			
		} catch (CtfActionException e) {
			e.printStackTrace();
		}
	
	}
	
	
	/** CELL UPDATES **/
	public static void processCellUpdatePacket(CtfPacket r) {
		try {
			CellUpdatePacket cup = new CellUpdatePacket( r.getBody() );
			/* update update state */
			CtfState.setUpdateState( r.getHeader().getUpdateState() );

			processCellUpdateBodies(cup);
			
			if ( CtfGui.getInstance() != null)
				CtfGui.getInstance().repaint();
			
		} catch (CtfActionException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Extract data from the cell update bodies
	 * @param cup
	 */
	public static void processCellUpdateBodies( CellUpdatePacket cup ) {
		byte[] cellUpdates = cup.getCellUpdatesInBytes();
		// for each cell update
		for( int i = 0; i < cup.getNumCellUpdates(); i++ ) {
			byte[] cellUpdate = Arrays.copyOfRange(cellUpdates, 
					i*CellUpdateBody.LENGTH, (i+1)*CellUpdateBody.LENGTH);
			try {
				CellUpdateBody cub = new CellUpdateBody( cellUpdate );
				//TODO
				
				System.out.println( "received cell update:");
				System.out.println( cup.getNumCellUpdates() );
				System.out.println( cub.toString() );
				
				// update player position, if any
				int player = cub.getOccupiedByPlayer();
				if ( player != 0) {
					// try to put in list
					if ( CtfState.getPlayerList().containsKey( new Integer(player) )) {
						Player pl = (Player) CtfState.getPlayerList().get(new Integer(player));
						pl.setPos(new Position( cub.getRow(), cub.getCol() ));
						CtfState.getPlayerList().put(new Integer(player), pl);							
					}
					else { // create new player
						Player pl = new Player( player, new Position( cub.getRow(), cub.getCol() ));
						CtfState.getPlayerList().put(new Integer(player), pl);	
					}
				}
				
				// set Maze object position, if any
				int objId = cub.getOccupiedByObject();
				switch ( objId ) {
					case CtfObject.TEAM1_FLAG_ID:
						Maze.setFlag1Pos( new Position(cub.getRow(), cub.getCol())); 
						break;
					case CtfObject.TEAM1_BOMBS_ID:
						Maze.setBomb1Pos( new Position(cub.getRow(), cub.getCol())); 
						break;
					case CtfObject.TEAM2_FLAG_ID:
						Maze.setFlag2Pos( new Position(cub.getRow(), cub.getCol())); 
						break;
					case CtfObject.TEAM2_BOMBS_ID:
						Maze.setBomb2Pos( new Position(cub.getRow(), cub.getCol())); 
						break;
				}
				// add the object to the player list, negated id
				if ( objId != 0) {
					objId = -1*objId;
					// try to put in list
					if ( CtfState.getPlayerList().containsKey( new Integer(objId) )) {
						Player pl = (Player) CtfState.getPlayerList().get(new Integer(player));
						pl.setPos(new Position( cub.getRow(), cub.getCol() ));
						CtfState.getPlayerList().put(new Integer(objId), pl);							
					}
					else { // create new player
						Player pl = new Player( objId, new Position( cub.getRow(), cub.getCol() ));
						CtfState.getPlayerList().put(new Integer(objId), pl);	
					}
				}
					
				
			} catch (CtfActionException e) {
				e.printStackTrace();
			}
		}
	}
	
	/** CELL UPDATES **/
	public static void processPlayerUpdatePacket(CtfPacket r) {
		
	}
	
	/** GAME UPDATES **/
	public static void processGameUpdatePacket(CtfPacket r) {
		GameUpdateBody gup = new GameUpdateBody( r.getBody() );
		
		/*********** Game Start **********************/
		// make sure that cell updates have been received prior to this
		CtfState.setGameState( GameStateTypes.STARTED );
		/* update update state */
		CtfState.setUpdateState( r.getHeader().getUpdateState() );
	}
	
	public static void processChatMsgBroadcast( CtfPacket r ) {
		ChatPacket cp = new ChatPacket(r.getBody());
	//	CtfChat.addMsg(cp.getMSg());
		CtfChat.addMsg(cp.getMyId(), cp.getMSg());
	}
	/**
	 * Initialize input stream for this thread.
	 * @param cdis
	 */
	public void initSocket(CtfDataInputStream cdis) {
		this.cdis = cdis;
	}

}
